/*
 *  Copyright 2011 Alexey Andreev.
 * 
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */
package org.xthl.wiki;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.nop.core.AbstractController;
import org.nop.core.Content;
import org.nop.core.RouteBinding;
import org.nop.forms.Paginator;
import org.nop.markup.MarkupRenderer;
import org.nop.markup.tree.MarkupText;
import org.nop.util.Injected;
import org.xthl.core.api.User;
import org.xthl.core.api.UserLogic;
import org.xthl.wiki.api.*;
import org.xthl.wiki.view.*;

/**
 *
 * @author Alexey Andreev
 */
@RouteBinding(ArticlesRoute.class)
public class ArticlesController extends AbstractController {
    private UserLogic userLogic;
    private ArticleLogic logic;
    private ArticleTextParser textParser;
    
    @Injected
    public ArticlesController(UserLogic userLogic, ArticleLogic logic,
            ArticleTextParser textParser) {
        this.userLogic = userLogic;
        this.logic = logic;
        this.textParser = textParser;
    }
    
    private ArticleSight getArticleSight(Article article) {
        ArticleSight sight = new ArticleSight();
        sight.setId(article.getId());
        sight.setPublicationDate(article.getPublicationDate());
        sight.setTitle(article.getTitle());
        MarkupText text = textParser.parseRevisionText(article.getLastPublishedRevisionId());
        sight.setPreview(new BriefRenderer(text, 500));
        return sight;
    }

    public Content list() {
        Paginator paginator = receiveForm(Paginator.class, "paginator");
        paginator.setItemsPerPage(20);        
        ArticleSearchCritery critery = new ArticleSearchCritery();
        critery.setPublishedOnly(true);
        critery.setDeletedFilter(DeletedObjectFilter.EXISTING_ONLY);
        critery.setSkip(paginator.getFirst());
        critery.setLimit(100);
        List<Article> articles = logic.find(critery);
        paginator.setItemCount(paginator.getFirst() + articles.size());
        List<ArticleSight> sightList = new ArrayList<ArticleSight>();
        for (Article article : articles.subList(0, paginator.getLast() - paginator.getFirst())) {
            sightList.add(getArticleSight(article));
        }
        return html(createView(ArticleListView.class)
                .setArticles(sightList)
                .setPaginator(paginator));
    }
    
    public Content search(ArticleSearchForm form) {
        Paginator paginator = form.getPaginator();
        paginator.setItemsPerPage(20);
        List<ArticleSight> sightList = new ArrayList<ArticleSight>();
        if (form.getSearchString() != null && !form.getSearchString().isEmpty()) {
            ArticleSearchByContentCritery critery = new ArticleSearchByContentCritery();
            critery.setOffset(paginator.getFirst());
            critery.setLimit(100);
            critery.setContentPart(form.getSearchString());
            List<Article> articles = logic.findByContent(critery);
            paginator.setItemCount(paginator.getFirst() + articles.size());
            for (Article article : articles.subList(0, paginator.getLast() -
                    paginator.getFirst())) {
                sightList.add(getArticleSight(article));
            }
        }
        return html(createView(ArticleSearchView.class)
                .setArticles(sightList)
                .setForm(form));
    }
    
    public Content view(int articleId) {
        final Article article = logic.get(articleId);
        if (article == null) {
            return null;
        }
        if (article.isDeleted()) {
            userLogic.requirePermission("wiki.listDeletedArticles");
        }
        int revisionId = article.getLastPublishedRevisionId() != null ?
                article.getLastPublishedRevisionId() : article.getLastRevisionId();
        MarkupRenderer content = new MarkupRenderer(textParser.parseRevisionText(revisionId));
        return html(createView(ArticleView.class)
                .setArticle(article)
                .setContent(content)
                .setEditable(userLogic.checkPermission("wiki.editArticle")));
    }
    
    public Content viewRevision(int articleId, int revisionNumber) {
        Revision revision = logic.getRevisionByNumber(articleId, revisionNumber);
        if (revision == null) {
            return null;
        }
        boolean canPublish = userLogic.checkPermission("wiki.publishArticle");
        MarkupRenderer content = new MarkupRenderer(textParser.parseRevisionText(
                revision.getId()));
        return html(createView(RevisionView.class)
                .setRevision(revision)
                .setPublicationAvailable(canPublish)
                .setContent(content));
    }
    
    public Content publish(int articleId, int revisionNumber) {
        Revision revision = logic.getRevisionByNumber(articleId, revisionNumber);
        if (revision == null) {
            return null;
        }
        logic.publish(revision.getId());
        return redirect(route(ArticlesRoute.class).view(revision.getArticleId()));
    }
    
    private List<String> splitToLines(String text) {
        if (text.isEmpty()) {
            return Collections.emptyList();
        }
        return Arrays.asList(text.split("\\r\\n|\\r|\\n", -1));
    }
    
    public Content diff(int articleId, int firstNum, int lastNum) {
        if (lastNum <= firstNum) {
            return null;
        }
        Article article = logic.get(articleId);
        if (article == null) {
            return null;
        }
        Revision first = logic.getRevisionByNumber(articleId, firstNum);
        Revision last = logic.getRevisionByNumber(articleId, lastNum);
        if (first == null || last == null) {
            return null;
        }
        List<String> diff = splitToLines(logic.getDiff(first.getId(), last.getId()));
        List<String> original = splitToLines(logic.getRevisionText(first.getId()));
        return html(createView(DiffView.class)
                .setArticle(article)
                .setDiff(new DiffRenderer(original, diff))
                .setFirstRevision(first)
                .setLastRevision(last)
                .setEditable(userLogic.checkPermission("wiki.editArticle")));
    }
    
    public Content revisions(int articleId) {
        Article article = logic.get(articleId);
        if (article == null) {
            return null;
        }
        RevisionSearchCritery critery = new RevisionSearchCritery();
        critery.setArticleId(articleId);
        List<Revision> revisions = logic.findRevisions(critery);
        return html(createView(RevisionsByArticleView.class)
                .setArticle(article)
                .setRevisions(revisions)
                .setEditable(userLogic.checkPermission("wiki.editArticle")));
    }
    
    public Content allRevisions() {
        Paginator paginator = receiveForm(Paginator.class, "paginator");
        paginator.setItemsPerPage(20);
        RevisionSearchCritery critery = new RevisionSearchCritery();
        critery.setLimit(Math.min(2000, paginator.getCurrent() + 200));
        List<Revision> revisions = logic.findRevisions(critery);
        paginator.setItemCount(revisions.size());
        return html(createView(RevisionsView.class)
                .setRevisions(revisions.subList(paginator.getFirst(), paginator.getLast()))
                .setPaginator(paginator));
    }
    
    public Content revisionsByAuthor(int authorId) {
        User author = userLogic.getUser(authorId);
        if (author == null) {
            return null;
        }
        Paginator paginator = receiveForm(Paginator.class, "paginator");
        paginator.setItemsPerPage(20);
        RevisionSearchCritery critery = new RevisionSearchCritery();
        critery.setLimit(Math.min(2000, paginator.getCurrent() + 200));
        critery.setAuthorId(authorId);
        List<Revision> revisions = logic.findRevisions(critery);
        paginator.setItemCount(revisions.size());
        return html(createView(RevisionsByAuthorView.class)
                .setAuthor(author)
                .setPaginator(paginator)
                .setRevisions(revisions.subList(paginator.getFirst(), paginator.getLast())));
    }
    
    public Content add(NewArticleForm form) {
        userLogic.requirePermission("wiki.createArticle");
        return html(createView(NewArticleView.class)
                .setForm(form));
    }
    
    public Content create(NewArticleForm form) {
        ArticleCreationCommand command = new ArticleCreationCommand();
        command.setTitle(form.getTitle());
        int articleId = logic.create(command);
        return redirect(route(ArticlesRoute.class).edit(articleId));
    }
    
    public Content edit(ArticleEditForm form, int articleId) {
        userLogic.requirePermission("wiki.editArticle");
        final Article article = logic.get(articleId);
        if (article == null || article.isDeleted()) {
            return null;
        }
        if (!form.isPosted()) {
            Revision revision = logic.getRevision(article.getLastRevisionId());
            form.setTitle(revision.getTitle());
            form.setContent(logic.getRevisionText(article.getLastRevisionId()));
            form.setPreviousRevisionId(article.getLastRevisionId());
        }
        return html(createView(ArticleEditView.class)
                .setForm(form)
                .setConcurrentRevision(null)
                .setArticle(article));
    }
    
    public Content save(ArticleEditForm form, int articleId) {
        final Article article = logic.get(articleId);
        if (article == null || article.isDeleted()) {
            return null;
        }
        ArticleUpdateCommand command = new ArticleUpdateCommand();
        command.setTitle(form.getTitle());
        command.setContent(form.getContent());
        command.setNote(form.getNote() != null ? form.getNote() : "");
        command.setPreviousRevisionId(form.getPreviousRevisionId());
        try {
            Revision revision = logic.getRevision(logic.edit(command, null));
            return redirect(route(ArticlesRoute.class)
                    .viewRevision(articleId, revision.getNumber()));
        } catch (ArticleConflictException ex) {
            return null;
        }
    }
}
